package org.grayrabbit.cms.plugin;

import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.grayrabbit.cms.plugin.loaders.SinglePluginLoader;
import org.grayrabbit.cms.utils.ClassLoaderUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


public class DefaultModuleDescriptorFactory implements ModuleDescriptorFactory {
	private static final Logger log = LoggerFactory.getLogger(DefaultModuleDescriptorFactory.class);
	private Map<String, Class<? extends ModuleDescriptor>> moduleDescriptorClasses;
	private List<String> permittedModuleKeys = Collections.emptyList();

	public DefaultModuleDescriptorFactory() {
		this.moduleDescriptorClasses = new HashMap<String,Class<? extends ModuleDescriptor>>();
	}

	public ModuleDescriptor getModuleDescriptor(String type)	throws PluginParseException, IllegalAccessException,
			InstantiationException, ClassNotFoundException {
		if (shouldSkipModuleOfType(type)) {
		      return null;
		    }
		    Class<?> moduleDescriptorClazz = getModuleDescriptorClass(type);
		    if (moduleDescriptorClazz == null) {
		      throw new PluginParseException("Cannot find ModuleDescriptor class for plugin of type '" + type + "'.");
		    }
		    return (ModuleDescriptor)ClassLoaderUtils.loadClass(moduleDescriptorClazz.getName(), SinglePluginLoader.class).newInstance();
	}

	protected boolean shouldSkipModuleOfType(String type)
	  {
	    return (this.permittedModuleKeys != null) && (!this.permittedModuleKeys.isEmpty()) && (!this.permittedModuleKeys.contains(type));
	  }
	
	public void setModuleDescriptors(Map<String,String> moduleDescriptorClassNames)
	  {
	    for (Map.Entry<String,String> entry : moduleDescriptorClassNames.entrySet() )
	    {
	      Class<? extends ModuleDescriptor> descriptorClass = getClassFromEntry(entry);
	      if (descriptorClass != null)
	        addModuleDescriptor((String)entry.getKey(), descriptorClass);
	    }
	  }
	protected void addModuleDescriptor(String type, Class<? extends ModuleDescriptor> descriptorClass) {
		this.moduleDescriptorClasses.put(type, descriptorClass);
	}

	@SuppressWarnings("unchecked")
	private Class<? extends ModuleDescriptor> getClassFromEntry(Entry<String, String> entry) {
		if (shouldSkipModuleOfType((String) entry.getKey())) {
			return null;
		}
		Class<? extends ModuleDescriptor> descriptorClass = null;
		try {
//			descriptorClass =
				Class<?> clazz =  ClassLoaderUtils.loadClass( entry.getValue(), getClass());
			if (!ModuleDescriptor.class.isAssignableFrom(clazz)) {
				log.error("Configured plugin module descriptor class "
						+ entry.getValue()
						+ " does not inherit from ModuleDescriptor");
				descriptorClass = null;
			}else{
				descriptorClass = (Class<? extends ModuleDescriptor>)clazz;
			}
		} catch (ClassNotFoundException e) {
			log.error("Unable to add configured plugin module descriptor "
					+ entry.getKey() + ". Class not found: " + entry.getValue());
		}

		return descriptorClass;
	}

	public boolean hasModuleDescriptor(String paramString) {
		return false;
	}

	public Class<? extends ModuleDescriptor> getModuleDescriptorClass(String type) {
		return this.moduleDescriptorClasses.get(type);
	}

}
